  /*
   * File: iframeResizer.js
   * Desc: Force iframes to size to content.
   * Requires: iframeResizer.contentWindow.js to be loaded into the target frame.
   * Doc: https://github.com/davidjbradshaw/iframe-resizer
   * Author: David J. Bradshaw - dave@bradshaw.net
   * Contributor: Jure Mav - jure.mav@gmail.com
   * Contributor: Reed Dadoune - reed@dadoune.com
   */

  // eslint-disable-next-line sonarjs/cognitive-complexity, no-shadow-restricted-names
  (function (undefined) {
    if (typeof window === "undefined") return; // don't run for server side render

    var count = 0,
      logEnabled = false,
      hiddenCheckEnabled = false,
      msgHeader = "message",
      msgHeaderLen = msgHeader.length,
      msgId = "[iFrameSizer]", // Must match iframe msg ID
      msgIdLen = msgId.length,
      pagePosition = null,
      requestAnimationFrame = window.requestAnimationFrame,
      resetRequiredMethods = {
        max: 1,
        scroll: 1,
        bodyScroll: 1,
        documentElementScroll: 1,
      },
      settings = {},
      timer = null,
      defaults = {
        autoResize: true,
        bodyBackground: null,
        bodyMargin: null,
        bodyMarginV1: 8,
        bodyPadding: null,
        checkOrigin: true,
        inPageLinks: false,
        enablePublicMethods: true,
        heightCalculationMethod: "bodyOffset",
        id: "iFrameResizer",
        interval: 32,
        log: false,
        maxHeight: Infinity,
        maxWidth: Infinity,
        minHeight: 0,
        minWidth: 0,
        mouseEvents: true,
        resizeFrom: "parent",
        scrolling: false,
        sizeHeight: true,
        sizeWidth: false,
        warningTimeout: 5000,
        tolerance: 0,
        widthCalculationMethod: "scroll",
        onClose: function () {
          return true;
        },
        onClosed: function () {},
        onInit: function () {},
        onMessage: function () {
          warn("onMessage function not defined");
        },
        onMouseEnter: function () {},
        onMouseLeave: function () {},
        onResized: function () {},
        onScroll: function () {
          return true;
        },
      };

    function getMutationObserver() {
      return (
        window.MutationObserver ||
        window.WebKitMutationObserver ||
        window.MozMutationObserver
      );
    }

    function addEventListener(el, evt, func) {
      el.addEventListener(evt, func, false);
    }

    function removeEventListener(el, evt, func) {
      el.removeEventListener(evt, func, false);
    }

    function setupRequestAnimationFrame() {
      var vendors = ["moz", "webkit", "o", "ms"];
      var x;

      // Remove vendor prefixing if prefixed and break early if not
      for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) {
        requestAnimationFrame =
          window[vendors[x] + "RequestAnimationFrame"];
      }

      if (!requestAnimationFrame) {
        log("setup", "RequestAnimationFrame not supported");
      } else {
        // Firefox extension content-scripts have a globalThis object that is not the same as window.
        // Binding `requestAnimationFrame` to window allows the function to work and prevents errors
        // being thrown when run in that context, and should be a no-op in every other context.
        requestAnimationFrame = requestAnimationFrame.bind(window);
      }
    }

    function getMyID(iframeId) {
      var retStr = "Host page: " + iframeId;

      if (window.top !== window.self) {
        retStr =
          window.parentIFrame && window.parentIFrame.getId
            ? window.parentIFrame.getId() + ": " + iframeId
            : "Nested host page: " + iframeId;
      }

      return retStr;
    }

    function formatLogHeader(iframeId) {
      return msgId + "[" + getMyID(iframeId) + "]";
    }

    function isLogEnabled(iframeId) {
      return settings[iframeId] ? settings[iframeId].log : logEnabled;
    }

    function log(iframeId, msg) {
      output("log", iframeId, msg, isLogEnabled(iframeId));
    }

    function info(iframeId, msg) {
      output("info", iframeId, msg, isLogEnabled(iframeId));
    }

    function warn(iframeId, msg) {
      output("warn", iframeId, msg, true);
    }

    function output(type, iframeId, msg, enabled) {
      if (true === enabled && "object" === typeof window.console) {
        // eslint-disable-next-line no-console
        console[type](formatLogHeader(iframeId), msg);
      }
    }

    function iFrameListener(event) {
      function resizeIFrame() {
        function resize() {
          setSize(messageData);
          setPagePosition(iframeId);
          on("onResized", messageData);
        }

        ensureInRange("Height");
        ensureInRange("Width");

        syncResize(resize, messageData, "init");
      }

      function processMsg() {
        var data = msg.substr(msgIdLen).split(":");
        var height = data[1] ? parseInt(data[1], 10) : 0;
        var iframe = settings[data[0]] && settings[data[0]].iframe;
        var compStyle = getComputedStyle(iframe);

        return {
          iframe: iframe,
          id: data[0],
          height:
            height + getPaddingEnds(compStyle) + getBorderEnds(compStyle),
          width: data[2],
          type: data[3],
        };
      }

      function getPaddingEnds(compStyle) {
        if (compStyle.boxSizing !== "border-box") {
          return 0;
        }
        var top = compStyle.paddingTop
          ? parseInt(compStyle.paddingTop, 10)
          : 0;
        var bot = compStyle.paddingBottom
          ? parseInt(compStyle.paddingBottom, 10)
          : 0;
        return top + bot;
      }

      function getBorderEnds(compStyle) {
        if (compStyle.boxSizing !== "border-box") {
          return 0;
        }
        var top = compStyle.borderTopWidth
          ? parseInt(compStyle.borderTopWidth, 10)
          : 0;
        var bot = compStyle.borderBottomWidth
          ? parseInt(compStyle.borderBottomWidth, 10)
          : 0;
        return top + bot;
      }

      function ensureInRange(Dimension) {
        var max = Number(settings[iframeId]["max" + Dimension]),
          min = Number(settings[iframeId]["min" + Dimension]),
          dimension = Dimension.toLowerCase(),
          size = Number(messageData[dimension]);

        log(
          iframeId,
          "Checking " + dimension + " is in range " + min + "-" + max
        );

        if (size < min) {
          size = min;
          log(iframeId, "Set " + dimension + " to min value");
        }

        if (size > max) {
          size = max;
          log(iframeId, "Set " + dimension + " to max value");
        }

        messageData[dimension] = "" + size;
      }

      function isMessageFromIFrame() {
        function checkAllowedOrigin() {
          function checkList() {
            var i = 0,
              retCode = false;

            log(
              iframeId,
              "Checking connection is from allowed list of origins: " +
                checkOrigin
            );

            for (; i < checkOrigin.length; i++) {
              if (checkOrigin[i] === origin) {
                retCode = true;
                break;
              }
            }
            return retCode;
          }

          function checkSingle() {
            var remoteHost =
              settings[iframeId] && settings[iframeId].remoteHost;
            log(iframeId, "Checking connection is from: " + remoteHost);
            return origin === remoteHost;
          }

          return checkOrigin.constructor === Array
            ? checkList()
            : checkSingle();
        }

        var origin = event.origin,
          checkOrigin =
            settings[iframeId] && settings[iframeId].checkOrigin;

        if (
          checkOrigin &&
          "" + origin !== "null" &&
          !checkAllowedOrigin()
        ) {
          throw new Error(
            "Unexpected message received from: " +
              origin +
              " for " +
              messageData.iframe.id +
              ". Message was: " +
              event.data +
              ". This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains."
          );
        }

        return true;
      }

      function isMessageForUs() {
        return (
          msgId === ("" + msg).substr(0, msgIdLen) &&
          msg.substr(msgIdLen).split(":")[0] in settings
        ); // ''+Protects against non-string msg
      }

      function isMessageFromMetaParent() {
        // Test if this message is from a parent above us. This is an ugly test, however, updating
        // the message format would break backwards compatibility.
        var retCode =
          messageData.type in { true: 1, false: 1, undefined: 1 };

        if (retCode) {
          log(iframeId, "Ignoring init message from meta parent page");
        }

        return retCode;
      }

      function getMsgBody(offset) {
        return msg.substr(msg.indexOf(":") + msgHeaderLen + offset);
      }

      function forwardMsgFromIFrame(msgBody) {
        log(
          iframeId,
          "onMessage passed: {iframe: " +
            messageData.iframe.id +
            ", message: " +
            msgBody +
            "}"
        );

        on("onMessage", {
          iframe: messageData.iframe,
          message: JSON.parse(msgBody),
        });

        log(iframeId, "--");
      }

      function getPageInfo() {
        var bodyPosition = document.body.getBoundingClientRect(),
          iFramePosition = messageData.iframe.getBoundingClientRect();

        return JSON.stringify({
          iframeHeight: iFramePosition.height,
          iframeWidth: iFramePosition.width,
          clientHeight: Math.max(
            document.documentElement.clientHeight,
            window.innerHeight || 0
          ),
          clientWidth: Math.max(
            document.documentElement.clientWidth,
            window.innerWidth || 0
          ),
          offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10),
          offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10),
          scrollTop: window.pageYOffset,
          scrollLeft: window.pageXOffset,
          documentHeight: document.documentElement.clientHeight,
          documentWidth: document.documentElement.clientWidth,
          windowHeight: window.innerHeight,
          windowWidth: window.innerWidth,
        });
      }

      function sendPageInfoToIframe(iframe, iframeId) {
        function debouncedTrigger() {
          trigger(
            "Send Page Info",
            "pageInfo:" + getPageInfo(),
            iframe,
            iframeId
          );
        }
        debounceFrameEvents(debouncedTrigger, 32, iframeId);
      }

      function startPageInfoMonitor() {
        function setListener(type, func) {
          function sendPageInfo() {
            if (settings[id]) {
              sendPageInfoToIframe(settings[id].iframe, id);
            } else {
              stop();
            }
          }

          ["scroll", "resize"].forEach(function (evt) {
            log(id, type + evt + " listener for sendPageInfo");
            func(window, evt, sendPageInfo);
          });
        }

        function stop() {
          setListener("Remove ", removeEventListener);
        }

        function start() {
          setListener("Add ", addEventListener);
        }

        var id = iframeId; // Create locally scoped copy of iFrame ID

        start();

        if (settings[id]) {
          settings[id].stopPageInfo = stop;
        }
      }

      function stopPageInfoMonitor() {
        if (settings[iframeId] && settings[iframeId].stopPageInfo) {
          settings[iframeId].stopPageInfo();
          delete settings[iframeId].stopPageInfo;
        }
      }

      function checkIFrameExists() {
        var retBool = true;

        if (null === messageData.iframe) {
          warn(iframeId, "IFrame (" + messageData.id + ") not found");
          retBool = false;
        }
        return retBool;
      }

      function getElementPosition(target) {
        var iFramePosition = target.getBoundingClientRect();

        getPagePosition(iframeId);

        return {
          x: Math.floor(
            Number(iFramePosition.left) + Number(pagePosition.x)
          ),
          y: Math.floor(
            Number(iFramePosition.top) + Number(pagePosition.y)
          ),
        };
      }

      function scrollRequestFromChild(addOffset) {
        /* istanbul ignore next */ // Not testable in Karma
        function reposition() {
          pagePosition = newPosition;
          scrollTo();
          log(iframeId, "--");
        }

        function calcOffset() {
          return {
            x: Number(messageData.width) + offset.x,
            y: Number(messageData.height) + offset.y,
          };
        }

        function scrollParent() {
          if (window.parentIFrame) {
            window.parentIFrame["scrollTo" + (addOffset ? "Offset" : "")](
              newPosition.x,
              newPosition.y
            );
          } else {
            warn(
              iframeId,
              "Unable to scroll to requested position, window.parentIFrame not found"
            );
          }
        }

        var offset = addOffset
            ? getElementPosition(messageData.iframe)
            : { x: 0, y: 0 },
          newPosition = calcOffset();

        log(
          iframeId,
          "Reposition requested from iFrame (offset x:" +
            offset.x +
            " y:" +
            offset.y +
            ")"
        );

        if (window.top !== window.self) {
          scrollParent();
        } else {
          reposition();
        }
      }

      function scrollTo() {
        if (false !== on("onScroll", pagePosition)) {
          setPagePosition(iframeId);
        } else {
          unsetPagePosition();
        }
      }

      function findTarget(location) {
        function jumpToTarget() {
          var jumpPosition = getElementPosition(target);

          log(
            iframeId,
            "Moving to in page link (#" +
              hash +
              ") at x: " +
              jumpPosition.x +
              " y: " +
              jumpPosition.y
          );
          pagePosition = {
            x: jumpPosition.x,
            y: jumpPosition.y,
          };

          scrollTo();
          log(iframeId, "--");
        }

        function jumpToParent() {
          if (window.parentIFrame) {
            window.parentIFrame.moveToAnchor(hash);
          } else {
            log(
              iframeId,
              "In page link #" +
                hash +
                " not found and window.parentIFrame not found"
            );
          }
        }

        var hash = location.split("#")[1] || "",
          hashData = decodeURIComponent(hash),
          target =
            document.getElementById(hashData) ||
            document.getElementsByName(hashData)[0];

        if (target) {
          jumpToTarget();
        } else if (window.top !== window.self) {
          jumpToParent();
        } else {
          log(iframeId, "In page link #" + hash + " not found");
        }
      }

      function onMouse(event) {
        var mousePos = {};

        if (
          Number(messageData.width) === 0 &&
          Number(messageData.height) === 0
        ) {
          var data = getMsgBody(9).split(":");
          mousePos = {
            x: data[1],
            y: data[0],
          };
        } else {
          mousePos = {
            x: messageData.width,
            y: messageData.height,
          };
        }

        on(event, {
          iframe: messageData.iframe,
          screenX: Number(mousePos.x),
          screenY: Number(mousePos.y),
          type: messageData.type,
        });
      }

      function on(funcName, val) {
        return chkEvent(iframeId, funcName, val);
      }

      function actionMsg() {
        if (settings[iframeId] && settings[iframeId].firstRun) firstRun();

        switch (messageData.type) {
          case "close":
            closeIFrame(messageData.iframe);
            break;

          case "message":
            forwardMsgFromIFrame(getMsgBody(6));
            break;

          case "mouseenter":
            onMouse("onMouseEnter");
            break;

          case "mouseleave":
            onMouse("onMouseLeave");
            break;

          case "autoResize":
            settings[iframeId].autoResize = JSON.parse(getMsgBody(9));
            break;

          case "scrollTo":
            scrollRequestFromChild(false);
            break;

          case "scrollToOffset":
            scrollRequestFromChild(true);
            break;

          case "pageInfo":
            sendPageInfoToIframe(
              settings[iframeId] && settings[iframeId].iframe,
              iframeId
            );
            startPageInfoMonitor();
            break;

          case "pageInfoStop":
            stopPageInfoMonitor();
            break;

          case "inPageLink":
            findTarget(getMsgBody(9));
            break;

          case "reset":
            resetIFrame(messageData);
            break;

          case "init":
            resizeIFrame();
            on("onInit", messageData.iframe);
            break;

          default:
            if (
              Number(messageData.width) === 0 &&
              Number(messageData.height) === 0
            ) {
              warn(
                "Unsupported message received (" +
                  messageData.type +
                  "), this is likely due to the iframe containing a later " +
                  "version of iframe-resizer than the parent page"
              );
            } else {
              resizeIFrame();
            }
        }
      }

      function hasSettings(iframeId) {
        var retBool = true;

        if (!settings[iframeId]) {
          retBool = false;
          warn(
            messageData.type +
              " No settings for " +
              iframeId +
              ". Message was: " +
              msg
          );
        }

        return retBool;
      }

      function iFrameReadyMsgReceived() {
        // eslint-disable-next-line no-restricted-syntax, guard-for-in
        for (var iframeId in settings) {
          trigger(
            "iFrame requested init",
            createOutgoingMsg(iframeId),
            settings[iframeId].iframe,
            iframeId
          );
        }
      }

      function firstRun() {
        if (settings[iframeId]) {
          settings[iframeId].firstRun = false;
        }
      }

      var msg = event.data,
        messageData = {},
        iframeId = null;

      if ("[iFrameResizerChild]Ready" === msg) {
        iFrameReadyMsgReceived();
      } else if (isMessageForUs()) {
        messageData = processMsg();
        iframeId = messageData.id;
        if (settings[iframeId]) {
          settings[iframeId].loaded = true;
        }

        if (!isMessageFromMetaParent() && hasSettings(iframeId)) {
          log(iframeId, "Received: " + msg);

          if (checkIFrameExists() && isMessageFromIFrame()) {
            actionMsg();
          }
        }
      } else {
        info(iframeId, "Ignored: " + msg);
      }
    }

    function chkEvent(iframeId, funcName, val) {
      var func = null,
        retVal = null;

      if (settings[iframeId]) {
        func = settings[iframeId][funcName];

        if ("function" === typeof func) {
          retVal = func(val);
        } else {
          throw new TypeError(
            funcName + " on iFrame[" + iframeId + "] is not a function"
          );
        }
      }

      return retVal;
    }

    function removeIframeListeners(iframe) {
      var iframeId = iframe.id;
      delete settings[iframeId];
    }

    function closeIFrame(iframe) {
      var iframeId = iframe.id;
      if (chkEvent(iframeId, "onClose", iframeId) === false) {
        log(iframeId, "Close iframe cancelled by onClose event");
        return;
      }
      log(iframeId, "Removing iFrame: " + iframeId);

      try {
        // Catch race condition error with React
        if (iframe.parentNode) {
          iframe.parentNode.removeChild(iframe);
        }
      } catch (error) {
        warn(error);
      }

      chkEvent(iframeId, "onClosed", iframeId);
      log(iframeId, "--");
      removeIframeListeners(iframe);
    }

    function getPagePosition(iframeId) {
      if (null === pagePosition) {
        pagePosition = {
          x:
            window.pageXOffset !== undefined
              ? window.pageXOffset
              : document.documentElement.scrollLeft,
          y:
            window.pageYOffset !== undefined
              ? window.pageYOffset
              : document.documentElement.scrollTop,
        };
        log(
          iframeId,
          "Get page position: " + pagePosition.x + "," + pagePosition.y
        );
      }
    }

    function setPagePosition(iframeId) {
      if (null !== pagePosition) {
        window.scrollTo(pagePosition.x, pagePosition.y);
        log(
          iframeId,
          "Set page position: " + pagePosition.x + "," + pagePosition.y
        );
        unsetPagePosition();
      }
    }

    function unsetPagePosition() {
      pagePosition = null;
    }

    function resetIFrame(messageData) {
      function reset() {
        setSize(messageData);
        trigger("reset", "reset", messageData.iframe, messageData.id);
      }

      log(
        messageData.id,
        "Size reset requested by " +
          ("init" === messageData.type ? "host page" : "iFrame")
      );
      getPagePosition(messageData.id);
      syncResize(reset, messageData, "reset");
    }

    function setSize(messageData) {
      function setDimension(dimension) {
        if (!messageData.id) {
          log("undefined", "messageData id not set");
          return;
        }
        messageData.iframe.style[dimension] = messageData[dimension] + "px";
        log(
          messageData.id,
          "IFrame (" +
            iframeId +
            ") " +
            dimension +
            " set to " +
            messageData[dimension] +
            "px"
        );
      }

      function chkZero(dimension) {
        // FireFox sets dimension of hidden iFrames to zero.
        // So if we detect that set up an event to check for
        // when iFrame becomes visible.

        /* istanbul ignore next */ // Not testable in PhantomJS
        if (!hiddenCheckEnabled && "0" === messageData[dimension]) {
          hiddenCheckEnabled = true;
          log(
            iframeId,
            "Hidden iFrame detected, creating visibility listener"
          );
          fixHiddenIFrames();
        }
      }

      function processDimension(dimension) {
        setDimension(dimension);
        chkZero(dimension);
      }

      var iframeId = messageData.iframe.id;

      if (settings[iframeId]) {
        if (settings[iframeId].sizeHeight) {
          processDimension("height");
        }
        if (settings[iframeId].sizeWidth) {
          processDimension("width");
        }
      }
    }

    function syncResize(func, messageData, doNotSync) {
      /* istanbul ignore if */ // Not testable in PhantomJS
      if (
        doNotSync !== messageData.type &&
        requestAnimationFrame &&
        // including check for jasmine because had trouble getting spy to work in unit test using requestAnimationFrame
        !window.jasmine
      ) {
        log(messageData.id, "Requesting animation frame");
        requestAnimationFrame(func);
      } else {
        func();
      }
    }

    function trigger(calleeMsg, msg, iframe, id, noResponseWarning) {
      function postMessageToIFrame() {
        var target = settings[id] && settings[id].targetOrigin;
        log(
          id,
          "[" +
            calleeMsg +
            "] Sending msg to iframe[" +
            id +
            "] (" +
            msg +
            ") targetOrigin: " +
            target
        );
        iframe.contentWindow.postMessage(msgId + msg, target);
      }

      function iFrameNotFound() {
        warn(id, "[" + calleeMsg + "] IFrame(" + id + ") not found");
      }

      function chkAndSend() {
        if (
          iframe &&
          "contentWindow" in iframe &&
          null !== iframe.contentWindow
        ) {
          // Null test for PhantomJS
          postMessageToIFrame();
        } else {
          iFrameNotFound();
        }
      }

      function warnOnNoResponse() {
        function warning() {
          if (settings[id] && !settings[id].loaded && !errorShown) {
            errorShown = true;
            warn(
              id,
              "IFrame has not responded within " +
                settings[id].warningTimeout / 1000 +
                " seconds. Check iFrameResizer.contentWindow.js has been loaded in iFrame. This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning."
            );
          }
        }

        if (
          !!noResponseWarning &&
          settings[id] &&
          !!settings[id].warningTimeout
        ) {
          settings[id].msgTimeout = setTimeout(
            warning,
            settings[id].warningTimeout
          );
        }
      }

      var errorShown = false;

      id = id || iframe.id;

      if (settings[id]) {
        chkAndSend();
        warnOnNoResponse();
      }
    }

    function createOutgoingMsg(iframeId) {
      return (
        iframeId +
        ":" +
        settings[iframeId].bodyMarginV1 +
        ":" +
        settings[iframeId].sizeWidth +
        ":" +
        settings[iframeId].log +
        ":" +
        settings[iframeId].interval +
        ":" +
        settings[iframeId].enablePublicMethods +
        ":" +
        settings[iframeId].autoResize +
        ":" +
        settings[iframeId].bodyMargin +
        ":" +
        settings[iframeId].heightCalculationMethod +
        ":" +
        settings[iframeId].bodyBackground +
        ":" +
        settings[iframeId].bodyPadding +
        ":" +
        settings[iframeId].tolerance +
        ":" +
        settings[iframeId].inPageLinks +
        ":" +
        settings[iframeId].resizeFrom +
        ":" +
        settings[iframeId].widthCalculationMethod +
        ":" +
        settings[iframeId].mouseEvents
      );
    }

    function isNumber(value) {
      return typeof value === "number";
    }

    function setupIFrame(iframe, options) {
      function setLimits() {
        function addStyle(style) {
          var styleValue = settings[iframeId][style];
          if (Infinity !== styleValue && 0 !== styleValue) {
            iframe.style[style] = isNumber(styleValue)
              ? styleValue + "px"
              : styleValue;
            log(iframeId, "Set " + style + " = " + iframe.style[style]);
          }
        }

        function chkMinMax(dimension) {
          if (
            settings[iframeId]["min" + dimension] >
            settings[iframeId]["max" + dimension]
          ) {
            throw new Error(
              "Value for min" +
                dimension +
                " can not be greater than max" +
                dimension
            );
          }
        }

        chkMinMax("Height");
        chkMinMax("Width");

        addStyle("maxHeight");
        addStyle("minHeight");
        addStyle("maxWidth");
        addStyle("minWidth");
      }

      function newId() {
        var id = (options && options.id) || defaults.id + count++;
        if (null !== document.getElementById(id)) {
          id += count++;
        }
        return id;
      }

      function ensureHasId(iframeId) {
        if ("" === iframeId) {
          // eslint-disable-next-line no-multi-assign
          iframe.id = iframeId = newId();
          logEnabled = (options || {}).log;
          log(
            iframeId,
            "Added missing iframe ID: " + iframeId + " (" + iframe.src + ")"
          );
        }

        return iframeId;
      }

      function setScrolling() {
        log(
          iframeId,
          "IFrame scrolling " +
            (settings[iframeId] && settings[iframeId].scrolling
              ? "enabled"
              : "disabled") +
            " for " +
            iframeId
        );
        iframe.style.overflow =
          false === (settings[iframeId] && settings[iframeId].scrolling)
            ? "hidden"
            : "auto";
        switch (settings[iframeId] && settings[iframeId].scrolling) {
          case "omit":
            break;

          case true:
            iframe.scrolling = "yes";
            break;

          case false:
            iframe.scrolling = "no";
            break;

          default:
            iframe.scrolling = settings[iframeId]
              ? settings[iframeId].scrolling
              : "no";
        }
      }

      // The V1 iFrame script expects an int, where as in V2 expects a CSS
      // string value such as '1px 3em', so if we have an int for V2, set V1=V2
      // and then convert V2 to a string PX value.
      function setupBodyMarginValues() {
        if (
          "number" ===
            typeof (settings[iframeId] && settings[iframeId].bodyMargin) ||
          "0" === (settings[iframeId] && settings[iframeId].bodyMargin)
        ) {
          settings[iframeId].bodyMarginV1 = settings[iframeId].bodyMargin;
          settings[iframeId].bodyMargin =
            "" + settings[iframeId].bodyMargin + "px";
        }
      }

      function checkReset() {
        // Reduce scope of firstRun to function, because IE8's JS execution
        // context stack is borked and this value gets externally
        // changed midway through running this function!!!
        var firstRun = settings[iframeId] && settings[iframeId].firstRun,
          resetRequertMethod =
            settings[iframeId] &&
            settings[iframeId].heightCalculationMethod in
              resetRequiredMethods;

        if (!firstRun && resetRequertMethod) {
          resetIFrame({
            iframe: iframe,
            height: 0,
            width: 0,
            type: "init",
          });
        }
      }

      function setupIFrameObject() {
        if (settings[iframeId]) {
          settings[iframeId].iframe.iFrameResizer = {
            close: closeIFrame.bind(null, settings[iframeId].iframe),

            removeListeners: removeIframeListeners.bind(
              null,
              settings[iframeId].iframe
            ),

            resize: trigger.bind(
              null,
              "Window resize",
              "resize",
              settings[iframeId].iframe
            ),

            moveToAnchor: function (anchor) {
              trigger(
                "Move to anchor",
                "moveToAnchor:" + anchor,
                settings[iframeId].iframe,
                iframeId
              );
            },

            sendMessage: function (message) {
              message = JSON.stringify(message);
              trigger(
                "Send Message",
                "message:" + message,
                settings[iframeId].iframe,
                iframeId
              );
            },
          };
        }
      }

      // We have to call trigger twice, as we can not be sure if all
      // iframes have completed loading when this code runs. The
      // event listener also catches the page changing in the iFrame.
      function init(msg) {
        function iFrameLoaded() {
          trigger("iFrame.onload", msg, iframe, undefined, true);
          checkReset();
        }

        function createDestroyObserver(MutationObserver) {
          if (!iframe.parentNode) {
            return;
          }

          var destroyObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
              var removedNodes = Array.prototype.slice.call(
                mutation.removedNodes
              ); // Transform NodeList into an Array
              removedNodes.forEach(function (removedNode) {
                if (removedNode === iframe) {
                  closeIFrame(iframe);
                }
              });
            });
          });
          destroyObserver.observe(iframe.parentNode, {
            childList: true,
          });
        }

        var MutationObserver = getMutationObserver();
        if (MutationObserver) {
          createDestroyObserver(MutationObserver);
        }

        addEventListener(iframe, "load", iFrameLoaded);
        trigger("init", msg, iframe, undefined, true);
      }

      function checkOptions(options) {
        if ("object" !== typeof options) {
          throw new TypeError("Options is not an object");
        }
      }

      function copyOptions(options) {
        // eslint-disable-next-line no-restricted-syntax
        for (var option in defaults) {
          if (Object.prototype.hasOwnProperty.call(defaults, option)) {
            settings[iframeId][option] =
              Object.prototype.hasOwnProperty.call(options, option)
                ? options[option]
                : defaults[option];
          }
        }
      }

      function getTargetOrigin(remoteHost) {
        return "" === remoteHost ||
          null !== remoteHost.match(/^(about:blank|javascript:|file:\/\/)/)
          ? "*"
          : remoteHost;
      }

      function depricate(key) {
        var splitName = key.split("Callback");

        if (splitName.length === 2) {
          var name =
            "on" +
            splitName[0].charAt(0).toUpperCase() +
            splitName[0].slice(1);
          this[name] = this[key];
          delete this[key];
          warn(
            iframeId,
            "Deprecated: '" +
              key +
              "' has been renamed '" +
              name +
              "'. The old method will be removed in the next major version."
          );
        }
      }

      function processOptions(options) {
        options = options || {};
        settings[iframeId] = {
          firstRun: true,
          iframe: iframe,
          remoteHost:
            iframe.src && iframe.src.split("/").slice(0, 3).join("/"),
        };

        checkOptions(options);
        Object.keys(options).forEach(depricate, options);
        copyOptions(options);

        if (settings[iframeId]) {
          settings[iframeId].targetOrigin =
            true === settings[iframeId].checkOrigin
              ? getTargetOrigin(settings[iframeId].remoteHost)
              : "*";
        }
      }

      function beenHere() {
        return iframeId in settings && "iFrameResizer" in iframe;
      }

      var iframeId = ensureHasId(iframe.id);

      if (!beenHere()) {
        processOptions(options);
        setScrolling();
        setLimits();
        setupBodyMarginValues();
        init(createOutgoingMsg(iframeId));
        setupIFrameObject();
      } else {
        warn(iframeId, "Ignored iFrame, already setup.");
      }
    }

    function debouce(fn, time) {
      if (null === timer) {
        timer = setTimeout(function () {
          timer = null;
          fn();
        }, time);
      }
    }

    var frameTimer = {};
    function debounceFrameEvents(fn, time, frameId) {
      if (!frameTimer[frameId]) {
        frameTimer[frameId] = setTimeout(function () {
          frameTimer[frameId] = null;
          fn();
        }, time);
      }
    }

    // Not testable in PhantomJS
    /* istanbul ignore next */

    function fixHiddenIFrames() {
      function checkIFrames() {
        function checkIFrame(settingId) {
          function chkDimension(dimension) {
            return (
              "0px" ===
              (settings[settingId] &&
                settings[settingId].iframe.style[dimension])
            );
          }

          function isVisible(el) {
            return null !== el.offsetParent;
          }

          if (
            settings[settingId] &&
            isVisible(settings[settingId].iframe) &&
            (chkDimension("height") || chkDimension("width"))
          ) {
            trigger(
              "Visibility change",
              "resize",
              settings[settingId].iframe,
              settingId
            );
          }
        }

        Object.keys(settings).forEach(function (key) {
          checkIFrame(key);
        });
      }

      function mutationObserved(mutations) {
        log(
          "window",
          "Mutation observed: " +
            mutations[0].target +
            " " +
            mutations[0].type
        );
        debouce(checkIFrames, 16);
      }

      function createMutationObserver() {
        var target = document.querySelector("body"),
          config = {
            attributes: true,
            attributeOldValue: false,
            characterData: true,
            characterDataOldValue: false,
            childList: true,
            subtree: true,
          },
          observer = new MutationObserver(mutationObserved);

        observer.observe(target, config);
      }

      var MutationObserver = getMutationObserver();
      if (MutationObserver) {
        createMutationObserver();
      }
    }

    function resizeIFrames(event) {
      function resize() {
        sendTriggerMsg("Window " + event, "resize");
      }

      log("window", "Trigger event: " + event);
      debouce(resize, 16);
    }

    // Not testable in PhantomJS
    /* istanbul ignore next */
    function tabVisible() {
      function resize() {
        sendTriggerMsg("Tab Visible", "resize");
      }

      if ("hidden" !== document.visibilityState) {
        log("document", "Trigger event: Visibility change");
        debouce(resize, 16);
      }
    }

    function sendTriggerMsg(eventName, event) {
      function isIFrameResizeEnabled(iframeId) {
        return (
          settings[iframeId] &&
          "parent" === settings[iframeId].resizeFrom &&
          settings[iframeId].autoResize &&
          !settings[iframeId].firstRun
        );
      }

      Object.keys(settings).forEach(function (iframeId) {
        if (isIFrameResizeEnabled(iframeId)) {
          trigger(eventName, event, settings[iframeId].iframe, iframeId);
        }
      });
    }

    function setupEventListeners() {
      addEventListener(window, "message", iFrameListener);

      addEventListener(window, "resize", function () {
        resizeIFrames("resize");
      });

      addEventListener(document, "visibilitychange", tabVisible);

      addEventListener(document, "-webkit-visibilitychange", tabVisible);
    }

    function factory() {
      function init(options, element) {
        function chkType() {
          if (!element.tagName) {
            throw new TypeError("Object is not a valid DOM element");
          } else if ("IFRAME" !== element.tagName.toUpperCase()) {
            throw new TypeError(
              "Expected <IFRAME> tag, found <" + element.tagName + ">"
            );
          }
        }

        if (element) {
          chkType();
          setupIFrame(element, options);
          iFrames.push(element);
        }
      }

      function warnDeprecatedOptions(options) {
        if (options && options.enablePublicMethods) {
          warn(
            "enablePublicMethods option has been removed, public methods are now always available in the iFrame"
          );
        }
      }

      var iFrames;

      setupRequestAnimationFrame();
      setupEventListeners();

      return function iFrameResizeF(options, target) {
        iFrames = []; // Only return iFrames past in on this call

        warnDeprecatedOptions(options);

        switch (typeof target) {
          case "undefined":
          case "string":
            Array.prototype.forEach.call(
              document.querySelectorAll(target || "iframe"),
              init.bind(undefined, options)
            );
            break;

          case "object":
            init(options, target);
            break;

          default:
            throw new TypeError(
              "Unexpected data type (" + typeof target + ")"
            );
        }

        return iFrames;
      };
    }

    function createJQueryPublicMethod($) {
      if (!$.fn) {
        info("", "Unable to bind to jQuery, it is not fully loaded.");
      } else if (!$.fn.iFrameResize) {
        $.fn.iFrameResize = function $iFrameResizeF(options) {
          function init(index, element) {
            setupIFrame(element, options);
          }

          return this.filter("iframe").each(init).end();
        };
      }
    }

    if (window.jQuery) {
      createJQueryPublicMethod(window.jQuery);
    }

    if (typeof define === "function" && define.amd) {
      define([], factory);
    } else if (
      typeof module === "object" &&
      typeof module.exports === "object"
    ) {
      // Node for browserfy
      module.exports = factory();
    }
    window.iFrameResize = window.iFrameResize || factory();
  })();